package com.feather.common.web;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

/**
 * @author flogyin
 */
public class JeRequest extends HttpServletRequestWrapper {
	protected Map<String, String[]>	params;
	protected ArgumentCustom		argumentCustom;

	public JeRequest(HttpServletRequest request) throws IOException {
		super(request);
	}

	@SuppressWarnings({ "rawtypes" })
	protected void parse() throws UnsupportedEncodingException {
		if (JeWeb.isMultipart(this)) {
			RequestEnvironment environment = RequestEnvironment.CURRENT[0];
			DiskFileItemFactory fileItemfactory = new DiskFileItemFactory();
			fileItemfactory.setRepository(new File(environment.getUploadTempDir(this)));
			fileItemfactory.setSizeThreshold(environment.getUploadBuffer(this));
			ServletFileUpload fileUpload = new ServletFileUpload(fileItemfactory);
			String encoding = environment.getRequestEncoding(this);
			if (encoding != null) {
				fileUpload.setHeaderEncoding(encoding);
			}
			int uploadMaxSize = environment.getUploadMaxSize(this);
			if (uploadMaxSize > 0) {
				fileUpload.setSizeMax(uploadMaxSize);
			}

			List itemList = null;
			try {
				itemList = fileUpload.parseRequest(this);
			} catch (Exception ex) {
				throw new RuntimeException(ex);
			}

			this.params = new LinkedHashMap<String, String[]>();
			this.params.putAll(super.getParameterMap());

			Iterator iterator = itemList.iterator();
			try {
				while (iterator.hasNext()) {
					FileItem fileItem = (FileItem) iterator.next();
					if (fileItem == null) {
						continue;
					}
					String fieldName = fileItem.getFieldName();
					if (fileItem.isFormField()) {
						String fieldString = encoding != null ? fileItem.getString(encoding) : fileItem.getString();
						if (fieldString == null) {
							fieldString = "";
						}
						this.params.put(fieldName, new String[] { fieldString });
					} else {
						List<XFileItem> uploads = new ArrayList<XFileItem>();
						uploads.add(new SimpleXFileItem(fileItem));
						JeWeb.setFilterUploads(this, uploads);
					}
				}
			} catch (IOException ex) {
				throw new RuntimeException(ex);
			}
		} else if (JeWeb.isIncludeRequest(this)) {
			this.params = new LinkedHashMap<String, String[]>();
			this.params.putAll(super.getParameterMap());

			String query = (String) getAttribute("javax.servlet.include.query_string");
			if (query != null) {
				HashMap<String, String[]> queryMap = new HashMap<String, String[]>();
				String charset = RequestEnvironment.CURRENT[0].getRequestEncoding(this);
				String[] parts = query.split("&");
				for (String part : parts) {
					String[] pair = part.split("=");
					if (pair[0].length() > 0) {
						String value = pair.length > 1 ? URLDecoder.decode(pair[1], charset) : "";

						String[] array = queryMap.get(pair[0]);
						String[] newArray = null;
						if (array == null) {
							newArray = new String[] { value };
						} else {
							newArray = new String[array.length + 1];
							System.arraycopy(array, 0, newArray, 0, array.length);
							newArray[array.length] = value;
						}
						queryMap.put(pair[0], newArray);
						this.params.put(pair[0], newArray);
					}
				}
			}
		} else if ("GET".equals(getMethod().toUpperCase())) {
			this.params = new LinkedHashMap<String, String[]>();
			this.params.putAll(super.getParameterMap());

			String charset = RequestEnvironment.CURRENT[0].getRequestEncoding(this);

			for (String[] values : this.params.values()) {
				int length = values.length;
				for (int i = 0; i < length; i++)
					values[i] = URLDecoder.decode(values[i], charset);
			}
		}
	}

	// //
	// // Wrapper
	// //
	@Override
	public Map<String, String[]> getParameterMap() {
		if (this.params == null) {
			try {
				parse();
			} catch (UnsupportedEncodingException ex) {
				throw new RuntimeException(ex);
			}
		}
		if (this.params == null) {
			this.params = new LinkedHashMap<String, String[]>();
			this.params.putAll(super.getParameterMap());
		}
		return this.params;
	}

	@Override
	public Enumeration<String> getParameterNames() {
		Map<String, String[]> map = getParameterMap();
		return Collections.enumeration(map.keySet());
	}

	@Override
	public String[] getParameterValues(String name) {
		Map<String, String[]> map = getParameterMap();
		return map.get(name);
	}

	@Override
	public String getParameter(String name) {
		String[] value = getParameterValues(name);
		return value != null ? (value.length > 0 ? value[0] : null) : null;
	}

	@Override
	public ServletInputStream getInputStream() throws IOException {
		ServletInputStream stream = super.getInputStream();
		String contentEncoding = super.getHeader("Content-Encoding");
		if (contentEncoding != null && contentEncoding.indexOf("gzip") != -1) {
			return new GzipServletInputStream(new GZIPInputStream(stream));
		}
		return stream;
	}

	// //
	// //util
	// //
	public void appendParameter(String name, String value) {
		if (value != null)
			appendParameter(name, new String[] { value });
	}

	public void appendParameter(String name, String[] value) {
		Map<String, String[]> map = getParameterMap();
		if (value != null & value.length > 0)
			map.put(name, value);
		else
			map.remove(name);
	}

	public void removeParameter(String name) {
		getParameterMap().remove(name);
	}
}
